package service.temp.job.matching.poc.listingapi;

import io.github.resilience4j.ratelimiter.RateLimiterConfig;
import lombok.extern.slf4j.Slf4j;
import me.xdrop.fuzzywuzzy.FuzzySearch;
import org.apache.commons.lang.StringUtils;
import service.temp.job.matching.poc.listingapi.dto.ListResult;
import service.temp.job.matching.poc.listingapi.dto.SearchItem;
import service.temp.job.matching.poc.listingapi.feign.FeignClientApiBuilder;
import utils.JsonUtil;

import org.codehaus.jackson.type.TypeReference;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.time.Duration;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
public class SearchApiPoc {

    public static GalaxySearchApi galaxySearchApi;

    public static SearchApi searchApi;

    private static final int EXECUTION_TIME_OUT_MS = 30_000;

    static {
        galaxySearchApi = buildGalaxySearchApi("74a5b105-e78e-4b68-8ec9-ea1b4d361503");
        searchApi = buildSearchApi("22dcfb0e-1804-4df7-b8bb-15c707cbcace");
    }


    public static void main(String[] args) {
//        Set<String> candidateSearchKeyWordList = loadCandidateSearchKeyWordList();
//        storeGalaxySearchResultToLocal(candidateSearchKeyWordList);
//        storeSearchResultToLocal(candidateSearchKeyWordList);

        doItemIdAnalysis();
        doProductTitleAnalysis();
    }

    public static void doItemIdAnalysis() {
        Map<String, List<SearchItem>> galaxySearchMapping = loadGalaxySearchResultFromLocal();
        Map<String, List<SearchItem>> searchMapping = loadSearchResultFromLocal();
        // 分析 返回的 itemId 的匹配度
        for (String keyword : galaxySearchMapping.keySet()) {
            if (searchMapping.containsKey(keyword)) {
                Set<Long> galaxyList = galaxySearchMapping.get(keyword).stream().map(SearchItem::getItemId).collect(Collectors.toSet());
                Set<Long> searchList = searchMapping.get(keyword).stream().map(SearchItem::getItemId).collect(Collectors.toSet());

                Set<Long> result = new HashSet<>();
                result.addAll(galaxyList);
                result.retainAll(searchList);
                int intersectionSize = result.size(); // 交集 size

                result.clear();
                result.addAll(galaxyList);
                result.addAll(searchList);
                int unionSize = result.size(); // 并集 size

                double curSimilarity = 100.0 * intersectionSize / unionSize;
                System.out.println("current keyword : " + keyword + " and match similarity : " + curSimilarity);
            }
        }
    }

    public static void doProductTitleAnalysis() {
        Map<String, List<SearchItem>> galaxySearchMapping = loadGalaxySearchResultFromLocal();
        Map<String, List<SearchItem>> searchMapping = loadSearchResultFromLocal();
        // 分析 返回的 ProductTitle 的匹配度
        for (String keyword : galaxySearchMapping.keySet()) {
            if (searchMapping.containsKey(keyword)) {
                final Map<Long, SearchItem> galaxyMap = galaxySearchMapping.get(keyword).stream().collect(Collectors.toMap(SearchItem::getItemId, a -> a, (k1, k2) -> k1));
                final Map<Long, SearchItem> searchMap = galaxySearchMapping.get(keyword).stream().collect(Collectors.toMap(SearchItem::getItemId, a -> a, (k1, k2) -> k1));

                Set<Long> intersectionKeySet = new HashSet<>(galaxyMap.keySet());
                intersectionKeySet.retainAll(searchMap.keySet());
                if (intersectionKeySet.size() <= 0) continue;

                int totalScore = 0;
                for (Long itemId : intersectionKeySet) {
                    String galaxyProductTitle = galaxyMap.get(itemId).getProductTitle();
                    String searchProductTitle = searchMap.get(itemId).getProductTitle();
                    int score = FuzzySearch.tokenSortRatio(galaxyProductTitle, searchProductTitle);
                    totalScore += score;
                }

                double curSimilarity = 1.0 * totalScore / intersectionKeySet.size();
                System.out.println("current keyword : " + keyword + " and match product title similarity : " + curSimilarity);
            }
        }
    }


    public static Map<String, List<SearchItem>> loadGalaxySearchResultFromLocal() {
        Map<String, List<SearchItem>> galaxyKeywordSearchMapping = new HashMap<>();
        String fileName = "/Users/zhfan/Downloads/galaxy_result.txt";
        galaxyKeywordSearchMapping = JsonUtil.readValue(new File(fileName), new TypeReference<Map<String, List<SearchItem>>>() {
        });

        return galaxyKeywordSearchMapping;
    }

    public static Map<String, List<SearchItem>> loadSearchResultFromLocal() {
        Map<String, List<SearchItem>> searchMapping = new HashMap<>();
        String fileName = "/Users/zhfan/Downloads/search_result.txt";
        searchMapping = JsonUtil.readValue(new File(fileName), new TypeReference<Map<String, List<SearchItem>>>() {
        });

        return searchMapping;
    }


    public static void storeGalaxySearchResultToLocal(Set<String> candidateSearchKeyWordList) {
        Map<String, List<SearchItem>> galaxyKeywordSearchMapping = new HashMap<>();
        for (String keyword : candidateSearchKeyWordList) {
            List<SearchItem> galaxySearchList = galaxySearchApi.searchItemByQuery(keyword);
            if (null != galaxySearchList) {
                galaxySearchList = galaxySearchList.subList(0, (galaxySearchList.size() >= 108 ? 108 : galaxySearchList.size()));
                galaxyKeywordSearchMapping.put(keyword, galaxySearchList);
                System.out.println("storeGalaxySearchResultToLocal had finish keyword : " + keyword);
            } else {
                System.err.println("storeGalaxySearchResultToLocal got null for keyword : " + keyword);
            }
        }

        try {
            BufferedWriter out = new BufferedWriter(new FileWriter("/Users/zhfan/Downloads/galaxy_result.txt"));
            out.write(JsonUtil.writeValueAsString(galaxyKeywordSearchMapping));
            out.close();
            System.out.println("GalaxySearchResultToLocal success！");
        } catch (Exception e) {
            System.err.println("storeGalaxySearchResultToLocal get error, cause : " + e);
        }
    }

    public static void storeSearchResultToLocal(Set<String> candidateSearchKeyWordList) {
        Map<String, List<SearchItem>> searchMapping = new HashMap<>();
        for (String keyword : candidateSearchKeyWordList) {
//            List<SearchItem> searchList = searchApi.searchItemByQuery(keyword);
            List<SearchItem> searchList = new ArrayList<>();
            if (null != searchList) {
                searchList = searchList.subList(0, (searchList.size() >= 108 ? 108 : searchList.size()));
                searchMapping.put(keyword, searchList);
                System.out.println("storeSearchResultToLocal had finish keyword : " + keyword);
            } else {
                System.err.println("storeSearchResultToLocal got null for keyword : " + keyword);
            }
        }

        try {
            BufferedWriter out = new BufferedWriter(new FileWriter("/Users/zhfan/Downloads/search_result.txt"));
            out.write(JsonUtil.writeValueAsString(searchMapping));
            out.close();
            System.out.println("GalaxySearchResultToLocal success！");
        } catch (Exception e) {
            System.err.println("storeGalaxySearchResultToLocal get error, cause : " + e);
        }
    }

    public static Set<String> loadCandidateSearchKeyWordList() {
        String fileName = "/Users/zhfan/Downloads/candidate_keyword.txt";
        BufferedReader br = null;
        String line = "";
        Set<String> candidateList = new HashSet<>();

        try {
            br = new BufferedReader(new FileReader(fileName));
            while ((line = br.readLine()) != null) {
                if (StringUtils.isBlank(line) || line.length() <= 1) continue;
                candidateList.add(line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return candidateList;
    }

    public static GalaxySearchApi buildGalaxySearchApi(String consumerToken) {
        Function<Exception, GalaxySearchApi> fallbackFactory = cause -> condition -> {
//            log.warn("call listing search api failed", cause);
            return new ListResult();
        };

        RateLimiterConfig rateLimiterConfig =
                RateLimiterConfig.custom()
                        .timeoutDuration(Duration.ofSeconds(60))  // timeout to fetch permission
                        .limitRefreshPeriod(Duration.ofSeconds(6))  // permissions limit refresh period
                        .limitForPeriod(90)  // permissions limit for period
                        .build();

        return FeignClientApiBuilder.buildFeignClientApi(GalaxySearchApi.class,
                consumerToken, EXECUTION_TIME_OUT_MS, rateLimiterConfig, fallbackFactory);
    }

    public static SearchApi buildSearchApi(String consumerToken) {
        Function<Exception, SearchApi> fallbackFactory = cause -> condition -> {
//            log.warn("call listing search api failed", cause);
            System.err.println("call listing search api failed, cause : " + cause);
            return new ListResult();
        };

        RateLimiterConfig rateLimiterConfig =
                RateLimiterConfig.custom()
                        .timeoutDuration(Duration.ofSeconds(60))  // timeout to fetch permission
                        .limitRefreshPeriod(Duration.ofSeconds(6))  // permissions limit refresh period
                        .limitForPeriod(90)  // permissions limit for period
                        .build();

        return FeignClientApiBuilder.buildFeignClientApi(SearchApi.class,
                consumerToken, EXECUTION_TIME_OUT_MS, rateLimiterConfig, fallbackFactory);
    }
}
